#!/usr/bin/env python

import os
import sys
import time
import pygame
import serial
import datetime
import subprocess
import pygame.locals
import multiprocessing

from ..._app import App
from _meta import Meta
from _config import Config

class App(App):
    '''App class
    
    @author Julot <andy.julot@gmail.com>
    @since Tuesday, November 20, 2012. 09:15 PM'''
    
    WIDTH = 1024
    HEIGHT = 700
    STEP = 5
    Y_0 = -5
    # max = (WIDTH - (Padding Top + Padding Bottom)) / STEP
    MAX = (700 / 5) + 2
    
    BLACK = (0, 0, 0)
    WHITE = (255, 255, 255)
    BLUE = (0, 0, 255)
    GREEN = (0, 255, 0)
    CYAN = (0, 255, 255)
    RED = (255, 0, 0)
    MAGENTA = (255, 0, 255)
    YELLOW = (255, 255, 0)
    
    HARDWARE_MISSING = 'Hardware Missing!'
    
    # Width of available canvas for each channel / Maximum data value
    MULTIPLIER = 954 / 1023.0
    
    _dataDir = ''
    
    def __init__(self):
        meta = Meta()
        config = {'Device': {'port': 'COM1', 'baudrate': 115200}}
        self.initConfig(meta.name, config)
        
        self._dataDir = os.path.realpath(os.path.join(self.homeDir, 'data'))
        if not os.path.lexists(self._dataDir): os.mkdir(self._dataDir)
    
    def _loadHardware(self):
        config = Config()
        
        try: port = config.get('Device', 'port')
        except: port = 'COM1'
        
        try: baudrate = config.getint('Device', 'baudrate')
        except: baudrate = 115200
        
        try: return serial.Serial(port, baudrate, timeout=.1)
        except: return None
    
    def _open(self):
        fileFormat = '{:04d}-{:02d}-{:02d} {:02d}.acl'
        now = datetime.datetime.now()
        name = fileFormat.format(now.year, now.month, now.day,
                                 now.hour)
        dataPath = os.path.join(self.homeDir, 'data', name)
        m = 'a' if os.path.lexists(dataPath) else 'w'
        return open(dataPath, m), now.hour
        
    def _readHardware(self, connection):
        hardware = None
        
        counter = 0
        totals = [0, 0, 0]
        divider = 20
        mode = 'stopped'
        dataFormat = '{:02d}:{:02d}:{:06.5f}\t{:04d}\n'
        while True:
            if connection.poll():
                message = connection.recv()
                if message in ('play', 'record'):
                    counter = 0
                    totals = [0, 0, 0]
                    mode = '%sing' % message
                    if mode == 'recording':
                        io, hour = self._open()
                elif message == 'stop':
                    if mode == 'recording': io.close()
                    mode = 'stopped'
                    hardware.setRTS(False)
                    hardware = None
                elif message == 'quit':
                    if mode == 'recording': io.close()
                    break
            
            if mode == 'stopped':
                time.sleep(0.1)
                continue
            
            if not hardware:
                hardware = self._loadHardware()
                if not hardware:
                    connection.send(('message', self.HARDWARE_MISSING))
                    mode = 'stopped'
                    continue
                else:
                    hardware.setRTS()
                    counter = 0
            
            lines = hardware.readline().strip().split('\t')
            if len(lines) != 3: continue
            
            try:
                counter += 1
                
                d1 = int(lines[0])
                d2 = int(lines[1])
                d3 = int(lines[2])
                
                totals[0] += d1
                totals[1] += d2
                totals[2] += d3
                
                if mode == 'recording':
                    now = datetime.datetime.now()
                    if hour != now.hour:
                        io.close()
                        io, hour = self._open()
                    second = float('%s.%s' % (now.second, now.microsecond))
                    data = dataFormat.format(now.hour, now.minute, second, d1)
                    io.write(data)
                
                if counter == divider:
                    d1 = int((totals[0] / divider) * self.MULTIPLIER)
                    d2 = int((totals[1] / divider) * self.MULTIPLIER)
                    d3 = int((totals[2] / divider) * self.MULTIPLIER)
                    
                    connection.send(('data', (d1, d2, d3)))
                    
                    counter = 0
                    totals = [0, 0, 0]
            except: pass
            
    def run(self):
        homeDir = os.path.realpath(os.path.dirname(sys.argv[0]))
        load = pygame.image.load
        
        parentConn, childConn = multiprocessing.Pipe()
        process = multiprocessing.Process(target=self._readHardware,
                                          args=(childConn,))
        
        colors = [self.RED, self.GREEN, self.BLUE]
        
        pygame.init()
        os.environ['SDL_VIDEO_CENTERED'] = '1'
        
        path = os.path.join('resources', 'images', 'single', 'icon.png')
        path = '%s%s%s' % (homeDir, os.path.sep, path)
        icon = load(path)
        pygame.display.set_icon(icon)
        screen = pygame.display.set_mode((self.WIDTH, self.HEIGHT), 0, 32)
        
        pygame.display.set_caption(Meta().description)
        
        clock = pygame.time.Clock()
        
        buttons = {'play': {}, 'record': {}, 'stop': {}, 'configure': {}}
        images = {'buttons': buttons}
        
##        path = os.path.join('resources', 'images', 'triple', 'background.png')
##        path = '%s%s%s' % (homeDir, os.path.sep, path)
##        images['background'] = load(path).convert_alpha()
        
        path = os.path.join('resources', 'buttons', 'enable', 'play.png')
        path = '%s%s%s' % (homeDir, os.path.sep, path)
        images['buttons']['play']['enable'] = load(path).convert_alpha()
        path = os.path.join('resources', 'buttons', 'disable', 'play.png')
        path = '%s%s%s' % (homeDir, os.path.sep, path)
        images['buttons']['play']['disable'] = load(path).convert_alpha()
        
        path = os.path.join('resources', 'buttons', 'enable', 'stop.png')
        path = '%s%s%s' % (homeDir, os.path.sep, path)
        images['buttons']['stop']['enable'] = load(path).convert_alpha()
        path = os.path.join('resources', 'buttons', 'disable', 'stop.png')
        path = '%s%s%s' % (homeDir, os.path.sep, path)
        images['buttons']['stop']['disable'] = load(path).convert_alpha()
        
        path = os.path.join('resources', 'buttons', 'enable', 'record.png')
        path = '%s%s%s' % (homeDir, os.path.sep, path)
        images['buttons']['record']['enable'] = load(path).convert_alpha()
        path = os.path.join('resources', 'buttons', 'disable', 'record.png')
        path = '%s%s%s' % (homeDir, os.path.sep, path)
        images['buttons']['record']['disable'] = load(path).convert_alpha()
        
        path = os.path.join('resources', 'buttons', 'enable', 'configure.png')
        path = '%s%s%s' % (homeDir, os.path.sep, path)
        images['buttons']['configure']['enable'] = load(path).convert_alpha()
        
        fonts = {'status': {'text': None}}
        
        path = os.path.join('resources', 'fonts', 'Impress BT.ttf')
        path = '%s%s%s' % (homeDir, os.path.sep, path)
        fonts['status']['font'] = pygame.font.Font(path, 80)
        
        running = True
        data = []
        mode = 'stopped'
        rects = {}
        # multiprocessing started
        started = False
        
        while running:
            for event in pygame.event.get():
                if event.type == pygame.locals.QUIT:
                    parentConn.send('quit')
                    running = False
                elif event.type == pygame.locals.MOUSEBUTTONDOWN:
                    buttons = pygame.mouse.get_pressed()
                    if buttons[0]:
                        # Left button clicked
                        pos = pygame.mouse.get_pos()
                        if rects['play'].collidepoint(pos):
                            if mode == 'stopped':
                                data = []
                                mode = 'playing'
                                if not started:
                                    process.start()
                                    started = True
                                parentConn.send('play')
                        elif rects['record'].collidepoint(pos):
                            if mode == 'stopped':
                                data = []
                                mode = 'recording'
                                if not started:
                                    process.start()
                                    started = True
                                parentConn.send('record')
                        elif rects['stop'].collidepoint(pos):
                            if mode != 'stopped':
                                mode = 'stopped'
                                parentConn.send('stop')
                        elif rects['configure'].collidepoint(pos):
                            notepad = os.path.join(self.homeDir, 'TedNPad.exe')
                            filename = '%s.conf' % Meta().name
                            path = os.path.join(self.homeDir, filename)
                            subprocess.Popen('%s %s' % (notepad, path))
                        if started: fonts['status']['text'] = None
            
            screen.fill(self.WHITE)
            
##            screen.blit(images['background'], (0, 0))
            
            state = 'enable' if mode == 'stopped' else 'disable'
            image = images['buttons']['play'][state]
            rects['play'] = screen.blit(image, (10, 10))
            
            state = 'enable' if mode == 'stopped' else 'disable'
            image = images['buttons']['record'][state]
            rects['record'] = screen.blit(image, (10, 68))
            
            state = 'disable' if mode == 'stopped' else 'enable'
            image = images['buttons']['stop'][state]
            rects['stop'] = screen.blit(image, (10, 126))
            
            image = images['buttons']['configure']['enable']
            rects['configure'] = screen.blit(image, (10, 700 - 58))
            
            if fonts['status']['text']:
                text = fonts['status']['text']
                width = text.get_width()
                height = text.get_height()
                x = ((956 - width) / 2) + 70
                screen.blit(text, (x, (self.HEIGHT - height) / 2))
            
            screen.lock()
            
            line = pygame.draw.aaline
            
            line(screen, self.BLACK, (68, 0), (68, 700))
            
            previousDatum = None
            y = self.Y_0
            # 70 because I draw line at 68
            addition = 70
            for datum in data:
                if previousDatum:
                    y += self.STEP
                    x = previousDatum[0] + addition
                    previous = (x, y - self.STEP)
                    x = datum[0] + addition
                    current = (x, y)
                    line(screen, self.BLUE, previous, current)
                previousDatum = datum
            
            screen.unlock()
            
            pygame.display.update()
            
            if parentConn.poll():
                state, message = parentConn.recv()
                if state == 'message':
                    status = fonts['status']
                    status['text'] = status['font'].render(message, True,
                                                           self.RED)
                    if message == self.HARDWARE_MISSING:
                        mode = 'stopped'
                elif state == 'data':
                    data.insert(0, message)
                    try: del data[self.MAX]
                    except: pass
            
            clock.tick(60)
            
        if started: process.join()
